package com.csx.micrometer.sentinel.core;

import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreaker;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.EventObserverRegistry;
import com.csx.micrometer.common.context.MonitorContext;
import com.csx.micrometer.sentinel.config.SentinelMicrometerAutoConfiguration;
import com.csx.micrometer.sentinel.constant.SentinelMetricsConstant;
import io.micrometer.core.instrument.MeterRegistry;
import io.micrometer.core.instrument.Tag;
import io.micrometer.core.instrument.binder.MeterBinder;

import java.util.Collections;
import java.util.List;

/**
 * 收集Sentinel熔断状态的指标
 * @author cuisongxu
 * @date 2023/2/1 14:00
 */
public class SentinelDegradeState implements MeterBinder {

    private final SentinelMicrometerAutoConfiguration.ApiDegradeStatus apiDegradeStatus;

    public SentinelDegradeState(SentinelMicrometerAutoConfiguration.ApiDegradeStatus apiDegradeStatus) {
        this.apiDegradeStatus = apiDegradeStatus;
    }

    /**
     * 注册熔断状态变更时的回调
     */
    public void registerDegradeStateChangeObserver() {

        EventObserverRegistry.getInstance().addStateChangeObserver("degradeState", (preState, curState, rule, tempValue) -> {
            this.bindTo(MonitorContext.get().getMeterRegistry());
            // 熔断开启，拒绝所有请求
            if(CircuitBreaker.State.OPEN.equals(curState)) { // 熔断开启
                apiDegradeStatus.getApiDegradeStatusMap().put(rule.getResource(), 0);
            }else if(CircuitBreaker.State.HALF_OPEN.equals(curState)) { // 熔断半开
                apiDegradeStatus.getApiDegradeStatusMap().put(rule.getResource(), 1);
            } else { // 熔断关闭
                apiDegradeStatus.getApiDegradeStatusMap().put(rule.getResource(), 2);
            }
        });
    }

    @Override
    public void bindTo(MeterRegistry registry) {
        //TODO 这样会存在一个问题, 由于规则大多是从配置中心获取到，因此当配置规则发生变更时，没有再次调用绑定方法，因此针对某些API的熔断降级状态就会失效
        List<DegradeRule> rules = DegradeRuleManager.getRules();
        for (DegradeRule rule : rules) {
            registry.gauge(SentinelMetricsConstant.SENTINEL_DEGRADE_STATE, Collections.singletonList(Tag.of(SentinelMetricsConstant.API, rule.getResource())), apiDegradeStatus, value -> value.getApiDegradeStatusMap().get(rule.getResource()) == null ? 2 : value.getApiDegradeStatusMap().get(rule.getResource()));
        }
    }
}
